
package util;

import model.PageTable;
import model.DiskBlock;
import model.Index;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * description: 模拟的操作系统的各部分功能以线程为基本运行单位,所有操作交给线程执行
 * @author wht
 * @createDate 2022/12/24 10:37
 */
public class ThreadUtil {
    /**
     * 控制只有一个线程访问内存
     */
    private static ReentrantLock lock = new ReentrantLock();

    /**
     * 等待队列，当线程阻塞后进入该队列
     */
    private static Condition waitQueue = lock.newCondition();

    /**
     * 创建固定核心数的线程池，这里核心数 = 5
     */
    private static ExecutorService executorService = Executors.newFixedThreadPool(5);

    /**
     * 数据生成线程
     */
    private  static Thread dataGenerationThread;

    /**
     * 删除数据线程
     */
    private static Thread deleteDataThread;

    /**
     * 执行线程
     */
    private static Thread executionThread;

    /**
     * 记录操作内存的线程占用情况
     */
    static private Map<String,Boolean> threadMap;

    /**
     * 记录总的线程占用情况
     */
    static private Map<String,Integer> currentThread;

    static final String DataGeneration = "dataGenerationThread";
    static final String DeleteData = "deleteDataThread";
    static final String Execution = "executionThread";
    static {
        threadMap = new ConcurrentHashMap<>();
        currentThread = new ConcurrentHashMap<>();
        threadMap.put(Execution,false);
        currentThread.put(DataGeneration,0);
        currentThread.put(DeleteData,0);
        currentThread.put(Execution,0);
    }

    /**
     * 生成外存数据
     * @param runnable
     * @return 是否执行成功
     */
    public static Boolean generateExternalDataExecute(Runnable runnable) {
        dataGenerationThread = new Thread(runnable);
        //统计当前线程情况
        currentThread.put(DataGeneration,currentThread.get(DataGeneration)+1);
        executorService.submit(dataGenerationThread);
        //执行完毕
        currentThread.put(DataGeneration,currentThread.get(DataGeneration)-1);
        return true;
    }

    //带有返回值的生成数据线程执行
    public static Object generateExternalDataExecute(FutureTask futureTask) {
        dataGenerationThread = new Thread(futureTask);
        currentThread.put(DataGeneration,currentThread.get(DataGeneration)+1);
        executorService.submit(dataGenerationThread);
        Object res = null;
        try {
            res = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            currentThread.put(DataGeneration,currentThread.get(DataGeneration)-1);
        }
        return res;
    }

    /**
     * 删除数据
     * @param runnable
     * @return
     */
    public static Boolean deleteDataExecute(Runnable runnable) {
        deleteDataThread = new Thread(runnable);
        currentThread.put(DeleteData,currentThread.get(DeleteData)+1);
        executorService.submit(deleteDataThread);
        currentThread.put(DeleteData,currentThread.get(DeleteData)-1);
        return true;
    }

    //带有返回值的删除数据线程执行
    public static Object deleteDataExecute(FutureTask futureTask) {
        deleteDataThread = new Thread(futureTask);
        currentThread.put(DeleteData,currentThread.get(DeleteData)+1);
        executorService.submit(deleteDataThread);
        Object res = null;
        try {
            res = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            currentThread.put(DeleteData,currentThread.get(DeleteData)-1);
        }
        return res;
    }

    /**
     * 执行线程将文件数据从外存调入内存
     * 保证同一时刻只有一个线程访问内存
     * @param runnable
     * @return 是否执行成功
     */
    public static Boolean memoryExecute(Runnable runnable){
        //先查看有没有占用内存的线程
        Boolean executionThread = threadMap.get(Execution);
        try {
            lock.lock();
            if (executionThread) {
                //当前内存正在被其他线程占用，进入阻塞队列
                waitQueue.await();
            }else {
                threadMap.put(Execution,true);
                currentThread.put(Execution,1);
                executorService.submit(runnable);
                //执行完毕后唤醒等待线程
                waitQueue.signal();
                threadMap.put(Execution,false);
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            currentThread.put(Execution,0);
            lock.unlock();
        }
        return executionThread;
    }

    /**
     * 带有返回值的操作内存线程执行
     * @param futureTask
     * @return 执行结果
     */
    public static Object memoryExecute(FutureTask futureTask) {
        //先查看有没有占用内存的线程
        Boolean executionThread = threadMap.get(Execution);
        try {
            lock.lock();
            if (executionThread) {
                //当前内存正在被其他线程占用，进入阻塞队列
                waitQueue.await();
            }else {
                threadMap.put(Execution,true);
                currentThread.put(Execution,1);
                executorService.submit(futureTask);
                //执行完毕后唤醒等待线程
                waitQueue.signal();
                threadMap.put(Execution,false);
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            lock.unlock();
        }
        Object res = null;
        try {
            res = futureTask.get();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            currentThread.put(Execution,0);
        }
        return res;
    }

    /**
     * 获取当前正在执行的线程数量
     * @return
     */
    public static Integer getCurrentThreadNum() {
        Integer res = 0;
        Iterator<Map.Entry<String, Integer>> iterator = currentThread.entrySet().iterator();
        while (iterator.hasNext()) {
            res += iterator.next().getValue();
        }
        return res;
    }

    /**
     * 获取当前的线程情况
     * 如
     *    dataGenerationThread:2
     *    deleteDataThread:0
     *    executionThread:1
     * @return
     */
    public static String getAllExecThread() {
        String res = "";
        Iterator<Map.Entry<String, Integer>> iterator = currentThread.entrySet().iterator();
        while (iterator.hasNext()) {
            res += iterator.next().getKey()+":"+iterator.next().getValue()+"\n";
        }
        return res;
    }

    public static void closeThreadPool() {
        executorService.shutdown();
    }

    /**
     * 以下为使用样例
     */
    public void example() {
        ThreadUtil.memoryExecute(()->{
            //从外存调入内存
            //分配内存
            //更新视图必须用Platform.runLater
//            Platform.runLater(new ReadThread());
        });

        //带有返回值的
        Object o = ThreadUtil.memoryExecute(new FutureTask(() -> {
            return "aaa";
        }));

        System.out.println(o);
    }

    public static class ReadThread implements Runnable{

        DiskBlock[] diskBlocks;
        Index index;
        PageTable pageTable;
        int startMemory;

        public ReadThread(Index index) {
            this.index = index;
        }

        @Override
        public void run() {
            pageTable = new PageTable();
            diskBlocks = null;

            Map<Integer, Integer> indexMap = index.getIndexMap();
//            startMemory = allocateMemory(diskBlocks, pageTable);
        }
    }

}
